package com.siao.component.scroller
{
	import com.siao.component.Orientation;
	import com.siao.display.LayoutElement;
	import com.siao.utils.math.round;
	
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	
	[Event(name="change", type="flash.events.Event")]
	
	/**
	 * 滚动条组件
	 * @author SiaoLeon
	 */
	public class ScrollBar extends LayoutElement
	{
		// -------------------------------------------------------------------------------------------------------- 静态常量
		
		// -------------------------------------------------------------------------------------------------------- 静态变量
		
		// -------------------------------------------------------------------------------------------------------- 静态方法
		
		// -------------------------------------------------------------------------------------------------------- 静态函数
		
		// -------------------------------------------------------------------------------------------------------- 属性
		
		private var _width:Number;
		
		override public function get width():Number
		{
			if (isNaN(this._width))
			{
				if (this.orientation == Orientation.HORIZONTAL)
				{
					return (super.width == 0) ? this.background.width + this.prevButton.width + this.nextButton.width + this.spacing * 2 : super.width;
				}
				else
				{
					return Math.max(this.background.width, this.drag.width, this.prevButton.width, this.nextButton.width);
				}
			}
			
			return this._width;
		}
		
		override public function set width(value:Number):void
		{
			this._width = super.width = value;
		}
		
		private var _height:Number;
		
		override public function get height():Number
		{
			if (isNaN(this._height))
			{
				if (this.orientation == Orientation.VERTICAL)
				{
					return (super.height == 0) ? this.background.height + this.prevButton.height + this.nextButton.height + this.spacing * 2 : super.height;
				}
				else
				{
					return Math.max(this.background.height, this.drag.height, this.prevButton.height, this.nextButton.height);
				}
			}
			
			return this._height;
		}
		
		override public function set height(value:Number):void
		{
			this._height = super.height = value;
		}
		
		/**
		 * 鼠标按下时等待移动延迟时间
		 * @default 500
		 */
		public var moveDelay:Number = 500;
		
		/**
		 * 鼠标按住时滚动间隔时间
		 * @default 30
		 */
		public var moveInterval:Number = 30;
		
		private var _thumbDrag:Boolean;
		
		/**
		 * 是否缩放拖拽滑块
		 * @return
		 */
		public function get thumbDrag():Boolean
		{
			return this._thumbDrag;
		}
		
		public function set thumbDrag(value:Boolean):void
		{
			this._thumbDrag = value;
			
			this.init();
		}
		
		private var _spacing:Number = 0;
		
		/**
		 * 滚动条间距
		 * @return
		 */
		public function get spacing():Number
		{
			return this._spacing;
		}
		
		public function set spacing(value:Number):void
		{
			this._spacing = value;
			
			this.init();
		}
		
		private var _orientation:uint;
		
		/**
		 * 滚动条方向
		 * @return
		 */
		public function get orientation():uint
		{
			return this._orientation;
		}
		
		public function set orientation(value:uint):void
		{
			this._orientation = value & 1;
			
			this.init();
		}
		
		private var _background:DisplayObject;
		
		/**
		 * 滚动条背景
		 * @return
		 */
		public function get background():DisplayObject
		{
			return (this._background == null) ? new Sprite() : this._background;
		}
		
		public function set background(value:DisplayObject):void
		{
			this.background.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
			
			this._background = value;
			
			this.layout();
		}
		
		private var _drag:DisplayObject;
		
		/**
		 * 滚动条滑块
		 * @return
		 */
		public function get drag():DisplayObject
		{
			return (this._drag == null) ? new Sprite() : this._drag;
		}
		
		public function set drag(value:DisplayObject):void
		{
			this.drag.removeEventListener(MouseEvent.MOUSE_DOWN, onDragMouseDown);
			
			this._drag = value;
			
			this.layout();
		}
		
		private var _prevButton:DisplayObject;
		
		/**
		 * 滚动条向前按钮
		 * @return
		 */
		public function get prevButton():DisplayObject
		{
			return (this._prevButton == null) ? new Sprite() : this._prevButton;
		}
		
		public function set prevButton(value:DisplayObject):void
		{
			this.prevButton.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
			
			this._prevButton = value;
			
			this.layout();
		}
		
		private var _nextButton:DisplayObject;
		
		/**
		 * 滚动条向后按钮
		 * @return
		 */
		public function get nextButton():DisplayObject
		{
			return (this._nextButton == null) ? new Sprite() : this._nextButton;
		}
		
		public function set nextButton(value:DisplayObject):void
		{
			this.nextButton.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
			
			this._nextButton = value;
			
			this.layout();
		}
		
		private var _minimum:Number = 0;
		
		/**
		 * 最小值
		 * @return
		 */
		public function get minimum():Number
		{
			return this._minimum;
		}
		
		public function set minimum(value:Number):void
		{
			this._minimum = value;
			
			this.init();
		}
		
		private var _maximum:Number = 0;
		
		/**
		 * 最大值
		 * @return
		 */
		public function get maximum():Number
		{
			return this._maximum;
		}
		
		public function set maximum(value:Number):void
		{
			this._maximum = value;
			
			this.init();
		}
		
		private var _amount:Number;
		
		/**
		 * 单位数量，此数值为最小调整数值，将影响背景点击的变更值及滑块尺寸，若不设置则关闭背景点击和滑块尺寸调整功能
		 * （但滑块尺寸不会小于其在100%缩放情况下的原始尺寸）
		 * @return
		 */
		public function get amount():Number
		{
			return this._amount;
		}
		
		public function set amount(value:Number):void
		{
			this._amount = value;
			
			this.init();
		}
		
		private var _unit:Number = 1;
		
		/**
		 * 单位数值，此数值为最小调整数值，将影响向前向后的变更值
		 * @return
		 */
		public function get unit():Number
		{
			return this._unit;
		}
		
		public function set unit(value:Number):void
		{
			this._unit = value;
		}
		
		private var _current:Number;
		
		/**
		 * 当前值，需要在最小值和最大值之后设定，并且不能超过最小值与最大值之间的范围。
		 * @return
		 */
		public function get current():Number
		{
			return isNaN(this._current) ? this.minimum : this._current;
		}
		
		public function set current(value:Number):void
		{
			this._current = (this.unitDragMode) ? Math.round(value / this.unit) * this.unit : value;
			this._current = (this._current > this.maximum && this.maximum >= this.minimum) ? this.maximum : (this._current < this.minimum && this.maximum > this.minimum) ? this.minimum : this._current;
			
			this.positionDrag();
		}
		
		private var _unitDragMode:Boolean;
		
		/**
		 * 是否开启单位滑块模式，该模式将以单位数值为最小滑块改变值
		 * @return
		 */
		public function get unitDragMode():Boolean
		{
			return this._unitDragMode;
		}
		
		public function set unitDragMode(value:Boolean):void
		{
			this._unitDragMode = value;
			
			this.current = this.current;
		}
		
		// -------------------------------------------------------------------------------------------------------- 变量
		
		private var dragLocalX:Number;
		
		private var dragLocalY:Number;
		
		private var downTimer:Timer;
		
		private var moveTimer:Timer;
		
		private var mouseDownTarget:DisplayObject;
		
		// -------------------------------------------------------------------------------------------------------- 构造
		
		/**
		 * 构造函数
		 * @param orientation 方向（水平、垂直）
		 * @param background 背景显示对象
		 * @param drag 滑块显示对象
		 * @param prevButton 向前按钮
		 * @param nextButton 向后按钮
		 */
		public function ScrollBar(orientation:uint=Orientation.VERTICAL, background:DisplayObject=null, drag:DisplayObject=null, prevButton:DisplayObject=null, nextButton:DisplayObject=null)
		{
			super();
			
			this._orientation = orientation & 1;
			this._background = background;
			this._drag = drag;
			this._prevButton = prevButton;
			this._nextButton = nextButton;
			
			this.downTimer = new Timer(this.moveDelay, 1);
			this.downTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerDown);
			this.moveTimer = new Timer(this.moveInterval, 0);
			this.moveTimer.addEventListener(TimerEvent.TIMER, onTimerMove);
		}
		
		// -------------------------------------------------------------------------------------------------------- 方法
		
		override public function resize():void
		{
			super.resize();
			
			if (this.drag.hasOwnProperty("enabled"))
			{
				if (this.drag["enabled"] != (this.maximum > this.minimum))
				{
					this.drag["enabled"] = (this.maximum > this.minimum);
				}
			}
			
			if (this.background.hasOwnProperty("enabled"))
			{
				if (this.background["enabled"] != (this.maximum > this.minimum))
				{
					this.background["enabled"] = (this.maximum > this.minimum);
				}
			}
			
			if (this.prevButton.hasOwnProperty("enabled"))
			{
				if (this.prevButton["enabled"] != (this.maximum > this.minimum))
				{
					this.prevButton["enabled"] = (this.maximum > this.minimum);
				}
			}
			
			if (this.nextButton.hasOwnProperty("enabled"))
			{
				if (this.nextButton["enabled"] != (this.maximum > this.minimum))
				{
					this.nextButton["enabled"] = (this.maximum > this.minimum);
				}
			}
			
			this.drag.scaleX = this.background.scaleX = this.prevButton.scaleX = this.nextButton.scaleX = 1;
			this.drag.scaleY = this.background.scaleY = this.prevButton.scaleY = this.nextButton.scaleY = 1;
			
			switch (this.orientation)
			{
				case Orientation.HORIZONTAL:
					this.drag.visible = (this.maximum > this.minimum && this.width > this.prevButton.width + this.drag.width + this.nextButton.width);
					
					this.background.width = Math.max(0, this.width - this.prevButton.width - this.nextButton.width - this.spacing * 2);
					this.drag.width = (this.drag.visible && this.thumbDrag) ? Math.max(this.drag.width, this.background.width * this.amount / (this.maximum - this.minimum + this.amount)) : this.drag.width;
					
					this.prevButton.width = Math.max(0, Math.min(this.width / 2, this.prevButton.width));
					this.nextButton.width = Math.max(0, Math.min(this.width / 2, this.nextButton.width));
					
					this.prevButton.x = 0;
					this.background.x = this.prevButton.width + this.spacing;
					this.nextButton.x = this.background.x + this.background.width + this.spacing;
					
					this.drag.y = (this.height - this.drag.height) / 2;
					this.background.y = (this.height - this.background.height) / 2;
					this.prevButton.y = (this.height - this.prevButton.height) / 2;
					this.nextButton.y = (this.height - this.nextButton.height) / 2;
					
					break;
				
				case Orientation.VERTICAL:
					this.drag.visible = (this.maximum > this.minimum && this.height > this.prevButton.height + this.drag.height + this.nextButton.height);
					
					this.background.height = Math.max(0, this.height - this.prevButton.height - this.nextButton.height - this.spacing * 2);
					this.drag.height = (this.drag.visible && this.thumbDrag) ? Math.max(this.drag.height, this.background.height * this.amount / (this.maximum - this.minimum + this.amount)) : this.drag.height;
					
					this.prevButton.height = Math.max(0, Math.min(this.height / 2, this.prevButton.height));
					this.nextButton.height = Math.max(0, Math.min(this.height / 2, this.nextButton.height));
					
					this.prevButton.y = 0;
					this.background.y = this.prevButton.height + this.spacing;
					this.nextButton.y = this.background.y + this.background.height + this.spacing;
					
					this.drag.x = (this.width - this.drag.width) / 2;
					this.background.x = (this.width - this.background.width) / 2;
					this.prevButton.x = (this.width - this.prevButton.width) / 2;
					this.nextButton.x = (this.width - this.nextButton.width) / 2;
					
					break;
			}
		}
		
		/**
		 * 设置数值
		 * @param min 最小值
		 * @param max 最大值
		 * @param unit 单位数值
		 * @param amount 单位数量
		 */
		public function setValue(min:Number, max:Number, unit:Number, amount:Number=Number.NaN):void
		{
			this._minimum = min;
			this._maximum = max;
			this._unit = unit;
			this._amount = amount;
			
			this.init();
		}
		
		/**
		 * 设置当前值，此方法将触发数值变更事件
		 * @param value 当前值
		 */
		public function setCurrent(value:Number):void
		{
			this.current = value;
			
			this.dispatchEvent(new Event(Event.CHANGE));
		}
		
		// -------------------------------------------------------------------------------------------------------- 函数
		
		override public function layout():void
		{
			this.clear();
			
			//将显示对象添加至舞台
			this.addChild(this.background);
			this.addChild(this.prevButton);
			this.addChild(this.nextButton);
			this.addChild(this.drag);
			
			super.layout();
		}
		
		override protected final function init():void
		{
			if (this.stage == null || !this.visible)
			{
				return;
			}
			
			super.init();
			
			this.positionDrag();
			this.registerEventListeners();
		}
		
		override protected final function exit():void
		{
			super.exit();
			
			if (this.downTimer.running)
			{
				this.downTimer.stop();
			}
			
			if (this.moveTimer.running)
			{
				this.moveTimer.stop();
			}
			
			if (this.stage != null)
			{
				this.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onDragMouseMove);
				this.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
				this.stage.removeEventListener(Event.MOUSE_LEAVE, onMouseLeave);
			}
			
			this.removeEventListeners();
		}
		
		/**
		 * 注册事件侦听
		 */
		private function registerEventListeners():void
		{
			this.drag.addEventListener(MouseEvent.MOUSE_DOWN, onDragMouseDown, false, 0, true);
			this.background.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver, false, 0, true);
			this.prevButton.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver, false, 0, true);
			this.nextButton.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver, false, 0, true);
		}
		
		/**
		 * 移除事件侦听
		 */
		private function removeEventListeners():void
		{
			this.drag.removeEventListener(MouseEvent.MOUSE_DOWN, onDragMouseDown);
			this.background.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
			this.prevButton.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
			this.nextButton.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
		}
		
		/**
		 * 定位滑块
		 */
		private function positionDrag():void
		{
			switch (this.orientation)
			{
				case Orientation.HORIZONTAL:
					var x:Number = this.background.x + (this.background.width - this.drag.width) * (this.current - this.minimum) / (this.maximum - this.minimum);
					this.drag.x = Math.max(0, x);
					break;
				
				case Orientation.VERTICAL:
					var y:Number = this.background.y + (this.background.height - this.drag.height) * (this.current - this.minimum) / (this.maximum - this.minimum);
					this.drag.y = Math.max(0, y);
					break;
			}
		}
		
		/**
		 * 鼠标经过时
		 * @param event
		 */
		private function onMouseOver(event:MouseEvent):void
		{
			if (this.mouseDownTarget != null && this.mouseDownTarget == event.target)
			{
				this.downTimer.start();
				this.moveTimer.start();
			}
			
			event.target.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut, false, 0, true);
			event.target.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, false, 0, true);
			this.stage.addEventListener(Event.MOUSE_LEAVE, onMouseLeave, false, 0, true);
		}
		
		/**
		 * 鼠标划出时
		 * @param event
		 */
		private function onMouseOut(event:MouseEvent):void
		{
			if (this.stage && this.mouseDownTarget == null)
			{
				this.stage.removeEventListener(Event.MOUSE_LEAVE, onMouseLeave);
			}
			
			event.target.removeEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
			event.target.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
			
			this.downTimer.stop();
			this.moveTimer.stop();
		}
		
		/**
		 * 鼠标离开舞台时
		 * @param event
		 */
		private function onMouseLeave(event:Event):void
		{
			this.mouseDownTarget = null;
			
			if (this.stage)
			{
				this.stage.removeEventListener(Event.MOUSE_LEAVE, onMouseLeave);
			}
			
			event.target.removeEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
			event.target.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
			
			this.downTimer.stop();
			this.moveTimer.stop();
		}
		
		/**
		 * 鼠标按下时
		 * @param event
		 */
		private function onMouseDown(event:MouseEvent):void
		{
			this.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp, false, 0, true);
			
			this.mouseDownTarget = event.target as DisplayObject;
			
			this.moveDrag(event.target as DisplayObject);
			
			this.downTimer.reset();
			this.downTimer.start();
		}
		
		/**
		 * 鼠标抬起时
		 * @param event
		 */
		private function onMouseUp(event:MouseEvent):void
		{
			this.mouseDownTarget = null;
			
			if (this.stage)
			{
				this.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
			}
			
			this.downTimer.stop();
			this.moveTimer.stop();
		}
		
		/**
		 * 鼠标按下计时器完成时
		 * @param event
		 */
		private function onTimerDown(event:TimerEvent):void
		{
			this.moveTimer.reset();
			this.moveTimer.start();
		}
		
		/**
		 * 滑块移动计时器间隔时
		 * @param event
		 */
		private function onTimerMove(event:TimerEvent):void
		{
			this.moveDrag(this.mouseDownTarget);
		}
		
		/**
		 * 移动滑块
		 * @param eventTarget 发生事件的显示对象
		 */
		private function moveDrag(eventTarget:DisplayObject):void
		{
			var change:Number = (eventTarget == this.background) ? this.amount : this.unit;
			
			switch (this.orientation)
			{
				case Orientation.HORIZONTAL:
					this.setCurrent((eventTarget == this.prevButton || this.background.mouseX * this.background.scaleX < this.drag.x) ? this.current - change : this.current + change);
					break;
				
				case Orientation.VERTICAL:
					this.setCurrent((eventTarget == this.prevButton || this.background.mouseY * this.background.scaleY < this.drag.y) ? this.current - change : this.current + change);
					break;
			}
		}
		
		/**
		 * 滑块鼠标按下时
		 * @param event
		 */
		private function onDragMouseDown(event:MouseEvent):void
		{
			this.stage.addEventListener(MouseEvent.MOUSE_MOVE, onDragMouseMove, false, 0, true);
			this.stage.addEventListener(MouseEvent.MOUSE_UP, onDragMouseUp, false, 0, true);
			this.stage.addEventListener(Event.MOUSE_LEAVE, onDragMouseUp, false, 0, true);
			
			this.dragLocalX = this.drag.mouseX * this.drag.scaleX;
			this.dragLocalY = this.drag.mouseY * this.drag.scaleY;
		}
		
		/**
		 * 滑块鼠标移动时
		 * @param event
		 */
		private function onDragMouseMove(event:MouseEvent):void
		{
			var x:Number = this.mouseX - this.dragLocalX;
			var y:Number = this.mouseY - this.dragLocalY;
			
			switch (this.orientation)
			{
				case Orientation.HORIZONTAL:
					this.drag.x = (x <= this.background.x) ? this.background.x : (x >= this.background.x + this.background.width - this.drag.width) ? round(this.background.x + this.background.width - this.drag.width, 2) : x;
					this.setCurrent(this.minimum + round(this.maximum - this.minimum, 2) * round(this.drag.x - this.background.x, 2) / round(this.background.width - this.drag.width, 2));
					break;
				
				case Orientation.VERTICAL:
					this.drag.y = (y <= this.background.y) ? this.background.y : (y >= this.background.y + this.background.height - this.drag.height) ? round(this.background.y + this.background.height - this.drag.height, 2) : y;
					this.setCurrent(this.minimum + round(this.maximum - this.minimum, 2) * round(this.drag.y - this.background.y, 2) / round(this.background.height - this.drag.height, 2));
					break;
			}
		}
		
		/**
		 * 滑块鼠标抬起时
		 * @param event
		 */
		private function onDragMouseUp(event:Event):void
		{
			if (this.stage != null)
			{
				this.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onDragMouseMove);
				this.stage.removeEventListener(MouseEvent.MOUSE_UP, onDragMouseUp);
				this.stage.removeEventListener(Event.MOUSE_LEAVE, onDragMouseUp);
			}
		}
	
	}
}
